using System;
using System.IO;
using System.Xml;
using Widgetsphere.Core.Util;
using Widgetsphere.Core.Logging;
using System.Collections;
using System.Collections.Generic;

namespace Widgetsphere.Core.DataAccess
{

  public partial class ConfigurationFile
  {
    #region Class's Log Switch
		private static readonly TraceSwitch mLogSwitch = LogClass.WSCommonDataAccess;
    #endregion

    #region Class Members

    private FileInfo mConnectionFile;
    private string mPassword = string.Empty;
    private bool mDirectConnect = true;
    private string mConfigString = string.Empty;

    public event EventHandler ConfigurationChanged;
    
    private const string CONFIGURATION_NODE = "Configuration";
    private const string ENCRYPTED_INFORMATION_NODE = "EncryptedInformation";
    private const string PASSOWRD_NODE = "Password";
    private const string WEB_SERVICE_LOCATION_NODE = "WebServiceLocation";
    private const string CONNECTION_NODE = "Connection";
    private const string DATABASE_CONNECTION_STRING_NODE = "UseDatabaseConnectionString";
    private const string DIRECT_CONNECT_NODE = "UseDirectConnection";
		private const string DEFAULT_CONFIG_FILE = @"config\company.config.xml";
    private const string APP_CONFIG_SETTING = "ConfigurationFile";
		private const string REG_CONFIG_LOCATION = @"Software\Widgetsphere\Company";
    private const string REG_PATH_ITEM = "SharePath";
    private const string REG_FILE_ITEM = "ConfigurationFile";
    private Hashtable _nameValuePairs = new Hashtable();

    #endregion

    #region Constructor

    public ConfigurationFile(string configFile)
    {
      InitilizeConfigurationFile(configFile);
    }

    public ConfigurationFile()
    {
      string fullFileName = GetConfigFilePath();
      InitilizeConfigurationFile(fullFileName);
      this.Open(mConnectionFile.CreationTimeUtc.ToLongDateString());
    }

    #endregion

    #region Properties

    public FileInfo ConnectionFile
    {
      get { return mConnectionFile; }
    }

    public string ConfigString
    {
      get { return mConfigString; }
      set { mConfigString = value; }
    }


    public bool DirectConnect
    {
      get { return mDirectConnect; }
      set { mDirectConnect = value; }
    }

    public Hashtable NameValuePairs
    {
      get { return _nameValuePairs; }
    }

    #endregion

    #region Methods

    private void InitilizeConfigurationFile(string fullFileName)
    {
      mConnectionFile = new FileInfo(fullFileName);
      if (!mConnectionFile.Exists)
        mConnectionFile.Create().Close();
      FileSystemWatcher watcher = new FileSystemWatcher(mConnectionFile.DirectoryName, mConnectionFile.Name);
      watcher.Changed += new FileSystemEventHandler(watcher_Changed);
    }

    private static string GetConfigFilePath()
    {
      string fullFileName = System.Configuration.ConfigurationManager.AppSettings[APP_CONFIG_SETTING];
      if (fullFileName == null || fullFileName == string.Empty)
      {
        string path = RegistryHelper.GetCurrentUserRegistryValue(REG_CONFIG_LOCATION, REG_PATH_ITEM);
        if (path == null || path == string.Empty)
        {
          path = RegistryHelper.GetLocalMachineRegistryValue(REG_CONFIG_LOCATION, REG_PATH_ITEM);
          string tmpFileName = RegistryHelper.GetCurrentUserRegistryValue(REG_CONFIG_LOCATION, REG_FILE_ITEM, DEFAULT_CONFIG_FILE);
          if (path != null && path != string.Empty)
            fullFileName = StringHelper.EnsureDirectorySeperatorAtEnd(path) + tmpFileName;
          else
            fullFileName = tmpFileName;
        }
        else
        {
          string tmpFileName = RegistryHelper.GetCurrentUserRegistryValue(REG_CONFIG_LOCATION, REG_FILE_ITEM, DEFAULT_CONFIG_FILE);
          fullFileName = StringHelper.EnsureDirectorySeperatorAtEnd(path) + tmpFileName;
        }
      }
      return fullFileName;
    }

    public void Save(string password)
    {
      mPassword = password;

      XmlTextWriter xWriter = new XmlTextWriter(mConnectionFile.FullName, System.Text.Encoding.UTF8);
      xWriter.WriteStartDocument();
      xWriter.WriteStartElement(CONFIGURATION_NODE);
      xWriter.WriteElementString(ENCRYPTED_INFORMATION_NODE, GetPropertiesAsEncryptedText());
      xWriter.WriteElementString(PASSOWRD_NODE, GetPasswordHash());
      xWriter.WriteEndElement();
      xWriter.WriteEndDocument();
      xWriter.Flush();
      xWriter.Close();
    }

    public bool Open(string password)
    {
      try
      {
        mPassword = password;
        XmlDocument oDoc = new XmlDocument();
        oDoc.Load(mConnectionFile.FullName);
        string encryptedInfo = oDoc.SelectSingleNode("//" + ENCRYPTED_INFORMATION_NODE).InnerText;
        string passwordHash = oDoc.SelectSingleNode("//" + PASSOWRD_NODE).InnerText;
        if (ValidPassword(passwordHash))
        {
          SetupPropertiesFromEncryptedText(encryptedInfo);
          return true;
        }
        else
        {
          return false;
        }
      }
      catch (Exception ex)
      {
        MessageLog.LogError(mLogSwitch, ex.ToString());
        throw;
      }
    }

    private string GetEntropy()
    {
      return "aujsywhdtgweysdjwiwlfnvtsfgzjxiked";
    }

    private string GetPropertiesAsEncryptedText()
    {
      ICryptoHelper crytographyHelper = CryptoFactory.Create(CryptographyAlgorithm.Rijndael);
      crytographyHelper.Entropy = GetEntropy();
      string encryptedText = crytographyHelper.Encrypt(GetPropertiesAsXml(), StringEncodingType.Hex);
      return encryptedText;
    }

    private void SetupPropertiesFromEncryptedText(string encryptedText)
    {
      ICryptoHelper crytographyHelper = CryptoFactory.Create(CryptographyAlgorithm.Rijndael);
      crytographyHelper.Entropy = GetEntropy();
      string decryptString = crytographyHelper.Decrypt(encryptedText, StringEncodingType.Hex);
      this.SetupProperties(decryptString);
    }


    private void SetupProperties(string propertiesAsXml)
    {
      XmlDocument oDoc = new XmlDocument();
      oDoc.LoadXml(propertiesAsXml);
      XmlNode directConnectNode = oDoc.SelectSingleNode("//" + DIRECT_CONNECT_NODE);
      DirectConnect = bool.Parse(directConnectNode.InnerText);
      if (this.DirectConnect)
      {
        XmlNode databaseConnectNode = oDoc.SelectSingleNode("//" + DATABASE_CONNECTION_STRING_NODE);
        ConfigString = databaseConnectNode.InnerText;
      }
      else
      {
        XmlNode webServiceNode = oDoc.SelectSingleNode("//" + WEB_SERVICE_LOCATION_NODE);
        ConfigString = webServiceNode.InnerText;
      }

      _nameValuePairs.Clear();
      foreach (XmlNode node in oDoc.DocumentElement.ChildNodes)
      {
        switch (node.Name)
        {
          case DIRECT_CONNECT_NODE:
          case DATABASE_CONNECTION_STRING_NODE:
          case WEB_SERVICE_LOCATION_NODE:
            break;
          default:
            _nameValuePairs.Add(node.Name, node.InnerText);
            break;
        }
      }

    }

    /// <summary>
    /// Add the saving of all properties here
    /// </summary>
    /// <returns></returns>
    private string GetPropertiesAsXml()
    {
      StringWriter sr = new StringWriter();
      XmlTextWriter xWriter = new XmlTextWriter(sr);
      xWriter.WriteStartElement(CONNECTION_NODE);
      xWriter.WriteElementString(DIRECT_CONNECT_NODE, this.DirectConnect.ToString());
      if (this.DirectConnect)
        xWriter.WriteElementString(DATABASE_CONNECTION_STRING_NODE, ConfigString);
      else
        xWriter.WriteElementString(WEB_SERVICE_LOCATION_NODE, ConfigString);

      //Write al key values
      foreach(string key in this.NameValuePairs.Keys)
      {
        xWriter.WriteElementString(key, (string)this.NameValuePairs[key]);
      }

      xWriter.WriteEndElement();
      xWriter.Flush();
      xWriter.Close();
      return sr.ToString();
    }

    private string GetPasswordHash()
    {
      HashHelper defaultHash = new HashHelper(HashHelper.ServiceProviderEnum.SHA256);
      return defaultHash.Encrypt(mPassword);
    }

    private bool ValidPassword(string passwordHash)
    {
      if (mPassword == mConnectionFile.CreationTimeUtc.ToLongDateString() ||
        StringHelper.Match(GetPasswordHash(), passwordHash))
      {
        return true;
      }
      return false;
    }

    #endregion

    #region Event Handlers

    protected void OnConfigurationChanged(object sender, System.EventArgs args)
    {
      if (ConfigurationChanged != null)
        ConfigurationChanged(sender, args);
    }

    private void watcher_Changed(object sender, FileSystemEventArgs e)
    {
      this.Open(mConnectionFile.CreationTimeUtc.ToLongDateString());
      OnConfigurationChanged(this, new System.EventArgs());
    }

    #endregion

  }
}
